package sebastiankerckhof.io.obj;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import sebastiankerckhof.io.ParserUtils;
import sebastiankerckhof.io.exception.ParseException;
import sebastiankerckhof.scene.Mesh;
import sebastiankerckhof.structure.TexCoord2f;
import sebastiankerckhof.structure.Triangle;
import sebastiankerckhof.structure.Vertex;
import sebastiankerckhof.structure.math.Point3f;
import sebastiankerckhof.structure.math.Vector3f;


public class ObjParser
{
    /**
     * The handlers for this parser
     */

    private List<ParserHandler> handlers = new ArrayList<ParserHandler>();
    
    private Scanner scanner;
    
  	private List<Point3f> coordinates = new ArrayList<Point3f>();
  	private List<TexCoord2f> textureCoordinates = new ArrayList<TexCoord2f>();
  	private List<Vector3f> normals = new ArrayList<Vector3f>();
  	private List<Triangle> triangles = new ArrayList<Triangle>();


    /**
     * Set the user's parser handler.
     * @param handler The user's parser handler.
     */

    public void setHandler(ParserHandler handler)
    {
        this.handlers.add(handler);
    }
    
    
    
    public Mesh parse(String name) throws NumberFormatException, ParseException, IOException{
      // create file and file input stream
      File file = new File("xml/" + name + ".obj");
    	return parse(file ,true,false);
    }
    
    /**
     * Parse a document and call the user's parser handler. 
     * @param input The input.
     * @param validate Indicates if the document needs to be validated. Set this to true.
     * @param echo Indicates wether the document needs to be echod to stdout.
     * @return a boolean indicating if the parse was successful.
     * @throws ParseException 
     * @throws IOException 
     * @throws NumberFormatException 
     */

    public Mesh parse(File f, boolean validate, boolean echo) throws ParseException, NumberFormatException, IOException
    {
    	
    	if(echo)
    		handlers.add(new EchoParserHandler());

    	  FileInputStream fstream = new FileInputStream(f);

    	  
    	  DataInputStream in = new DataInputStream(fstream);
    	  BufferedReader br = new BufferedReader(new InputStreamReader(in));
    	  String strLine;
    	  
    	  String type;
    	  while ((strLine = br.readLine()) != null)   {
    	  	type = strLine.substring(0, 2).trim();
    	  	
    	  	if(type.equals("v")){
    	  		coordinates.add(ParserUtils.parsePoint3f(strLine.substring(2)));
    	  	}else if(type.equals("vt")){
    	  		textureCoordinates.add(new TexCoord2f(ParserUtils.parseTexCoord2f(strLine.substring(2))));
    	  	}else if(type.equals("vn")){
    	  		normals.add(ParserUtils.parseVector3f(strLine.substring(2)));
    	  	}else if(type.equals("f")){
    	  		
    	  		Vertex[] vertexes = new Vertex[3];
    	  		System.out.println(strLine);
    	  		String[] split = strLine.substring(2).trim().split(" ");
    	  		if(split.length != 3 && validate){
    	  			throw new ParseException("Only models with triangles are supported at this moment");
    	  		}
    	  		
    	  		String[] innerSplit;
    	  		int i = 0;
    	  		for(String s : split){
    	  			innerSplit = s.split("/");
    	  			vertexes[i] = new Vertex(coordinates.get(Integer.parseInt(innerSplit[0]) - 1));
    	  			
    	  			if(innerSplit.length > 1){
    	  				if(!innerSplit[1].isEmpty() && innerSplit.length == 3)
    	  					vertexes[i].texture = textureCoordinates.get(Integer.parseInt(innerSplit[1]) - 1);
    	  				
    	  				vertexes[i].normal = normals.get(Integer.parseInt(innerSplit[innerSplit.length - 1]) - 1);
    	  			}
    	  			
    	  			i++;
    	  		} 
    	  		
    	  		triangles.add(new Triangle(vertexes));
    	  	}
    	  }

    	  in.close();
    	  
    	  Mesh m = new Mesh(triangles);
    	  for(ParserHandler h : handlers){
    	  	h.startMesh(m);
    	  }
    	  return m;
    	
    }
    
}

